﻿# -*- coding: utf-8 -*-
"""
实现一个 socket 服务端和客户端，客户端发送消息给服务端，
服务端返回客户端 ip+port 带上收到的消息返回 给客户端，支持客户端发送 'quit'消息，主动断开连接。
"""
import socket
import threading
import selectors
import logging
import datetime

FORMAT = '%(asctime)s %(threadName)s %(thread)d [%(message)s]'
logging.basicConfig(format=FORMAT, level=logging.INFO, datefmt='%H:%M:%S')


class Tcp_server:
    def __init__(self, ip='127.0.0.1', port=60000):
        self.laddr = ip, port
        self.sever = socket.socket()
        self.event = threading.Event()
        self.selector = selectors.DefaultSelector()

    def start(self):
        self.sever.bind(self.laddr)
        self.sever.listen()
        self.sever.setblocking(False)
        self.selector.register(self.sever, selectors.EVENT_READ, self.accept)
        threading.Thread(target=self.select, name='select').start()
        while True:
            In = input('>>>')
            if In == 'quit':
                self.stop()
                break
            else:
                print(
                    f'有 {len(list(filter(lambda i: i.data == self.recv, [i for i in self.selector.get_map().values()])))} 个用户连接！')

    def select(self):
        while not self.event.is_set():
            events = self.selector.select(0.5)
            for key, _ in events:
                key.data(key.fileobj)
        self.selector.close()

    def accept(self, sever: socket.socket):
        try:
            new_socket_clinet, raddr = sever.accept()
            new_socket_clinet.setblocking(False)
            self.selector.register(new_socket_clinet, selectors.EVENT_READ, self.recv)
        except Exception as e:
            logging.warning(e)

    def recv(self, new_client: socket.socket):
        client = new_client.getpeername()
        try:
            data = new_client.recv(1024).decode().strip()
        except Exception as e:
            logging.warning(e)
            data = ''
        if not data or data == 'quit':
            new_client.close()
            self.selector.unregister(new_client)
            logging.info('{}:{}下线了！'.format(*client))
            return
        msg = '{:%Y-%m-%d %H:%M:%S} From {}:{}--->{}'.format(datetime.datetime.now(),
                                                             *client,
                                                             data)
        print(msg)
        new_client.send(msg.encode())

    def stop(self):
        self.event.set()
        self.sever.close()


def run():
    ss = Tcp_server()
    ss.start()


if __name__ == '__main__':
    run()
